package org.iks.store.business.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.iks.common.ResultCode;
import org.iks.common.exception.BusinessException;
import org.iks.common.pojo.store.SecretKeyVo;
import org.iks.common.pojo.user.UserInfoVo;
import org.iks.common.util.ServletUtil;
import org.iks.feign.service.user.UserRemoteCaller;
import org.iks.store.business.entity.SessionEncipherEntity;
import org.iks.store.business.mapper.SessionEncipherMapper;
import org.iks.store.business.mapper.SessionMapper;
import org.iks.store.business.service.SessionEncipherService;
import org.iks.store.business.vo.secret.SecretCreateItemVo;
import org.iks.store.business.vo.secret.SecretCreateVo;
import org.iks.store.business.vo.session.SessionVo;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Author kzou
 * Date 2024/2/20
 * Description:
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SessionEncipherServiceImpl extends ServiceImpl<SessionEncipherMapper, SessionEncipherEntity> implements SessionEncipherService {

    private static final String lock_sql = "select * from schedule_lock where lock_name = 'encipher_add_lock' for update";

    private final DataSource dataSource;

    private final UserRemoteCaller userRemoteCaller;

    private final SessionMapper sessionMapper;


    @Override
    public SecretKeyVo queryLatestSecretKey(String userAccount, Integer sessionId) {
        return baseMapper.queryLatestSecretKey(userAccount, sessionId);
    }

    @Override
    public synchronized int createSecretKey(String userAccount, SecretCreateVo secretCreateVo) {
        Integer sessionId = secretCreateVo.getSessionId();
        log.info("create session key creatorAccount={} {}", userAccount, secretCreateVo);
        SessionVo sessionVo = sessionMapper.querySession(sessionId);
        if (sessionVo == null) {
            log.warn("invalid parameter sessionId {}", secretCreateVo);
            throw new BusinessException(ResultCode.PARAMETER_EXCEPTION);
        }
        // 加锁
        Connection conn = null;
        Boolean autoCommit = null;
        PreparedStatement preparedStatement = null;
        int result = 0;
        try {
            conn = dataSource.getConnection();
            autoCommit = conn.getAutoCommit();
            conn.setAutoCommit(false);
            preparedStatement = conn.prepareStatement(lock_sql);
            preparedStatement.execute();
            // update 逻辑
            Integer preSecretKeyVersion = secretCreateVo.getPreSecretKeyVersion();
            SessionEncipherEntity sessionEncipher = queryLatestVersionSecretKey(sessionId);
            if (sessionEncipher == null) {
                preSecretKeyVersion = 0;
            } else if (preSecretKeyVersion == -1) {
                preSecretKeyVersion = sessionEncipher.getSecretKeyVersion();
            } else if (!Objects.equals(sessionEncipher.getSecretKeyVersion(), preSecretKeyVersion)) {
                // 乐观锁失败
                return -1;
            }
            // 写入数据库
            Date dateNow = new Date();
            for (SecretCreateItemVo secretItem : secretCreateVo.getSecretItems()) {
                SessionEncipherEntity sessionEncipherEntity = new SessionEncipherEntity();
                sessionEncipherEntity.setSessionId(sessionId);
                sessionEncipherEntity.setCreatorAccount(userAccount);
                sessionEncipherEntity.setCreateTime(dateNow);
                sessionEncipherEntity.setSecretKeyVersion(preSecretKeyVersion + 1);
                sessionEncipherEntity.setUserAccount(secretItem.getUserAccount());
                sessionEncipherEntity.setSecretKey(secretItem.getSecretKey());
                result += baseMapper.insert(sessionEncipherEntity);
            }
        } catch (SQLException e) {
            log.error("lock sql execute error {}", e.getMessage());
        } finally {
            if (conn != null) {
                try {
                    // 提交事务
                    conn.commit();
                    if (autoCommit != null) {
                        conn.setAutoCommit(autoCommit);
                    }
                } catch (SQLException e) {
                    log.warn("setAutoCommit error {}", e.getMessage());
                    try {
                        conn.rollback();
                    } catch (SQLException ex) {
                        log.error("rollback exception {}", ex.getMessage());
                    }
                }
            }
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    log.warn("close preparedStatement error {}", e.getMessage());
                }
            }
        }
        return result;
    }

    @Override
    public SessionEncipherEntity queryLatestVersionSecretKey(Integer sessionId) {
        LambdaQueryWrapper<SessionEncipherEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SessionEncipherEntity::getSessionId, sessionId)
                .orderByDesc(SessionEncipherEntity::getSecretKeyVersion)
                .last("limit 1");
        return baseMapper.selectOne(queryWrapper);
    }

    @Override
    public SecretKeyVo queryByVersion(Integer sessionId, Integer version, String userAccount) {
        SecretKeyVo secretKeyVo = baseMapper.queryByVersion(sessionId, version, userAccount);
        if (secretKeyVo == null) {
            return null;
        }
        UserInfoVo userInfoVo = userRemoteCaller.queryUserInfo(secretKeyVo.getCreatorAccount());
        secretKeyVo.setCreatorInfo(userInfoVo);
        return secretKeyVo;
    }

    @Override
    public List<SecretKeyVo> queryListSecretKey(Integer sessionId) {
        String userAccount = ServletUtil.currUser().account();
        List<SecretKeyVo> secretKeyVos = baseMapper.querySecrets(sessionId, userAccount);
        Map<String, UserInfoVo> userInfoVoMap = Collections.emptyMap();
        if (!secretKeyVos.isEmpty()) {
            Set<String> accounts = secretKeyVos.stream().map(SecretKeyVo::getCreatorAccount).collect(Collectors.toSet());
            List<UserInfoVo> data = userRemoteCaller.queryUserInfoBatch(accounts);
            userInfoVoMap = data.stream().collect(Collectors.toMap(UserInfoVo::getAccount, e -> e));
        }
        for (SecretKeyVo secretKeyVo : secretKeyVos) {
            String creatorAccount = secretKeyVo.getCreatorAccount();
            secretKeyVo.setCreatorInfo(userInfoVoMap.get(creatorAccount));
        }
        return secretKeyVos;
    }

    @Override
    public SecretKeyVo queryById(Integer secretKeyId) {
        SecretKeyVo secretKeyVo = baseMapper.queryById(secretKeyId);
        UserInfoVo userInfoVo = userRemoteCaller.queryUserInfo(secretKeyVo.getCreatorAccount());
        secretKeyVo.setCreatorInfo(userInfoVo);
        return secretKeyVo;
    }

}
